#!/usr/bin/env python3
"""
MCPy 命令行工具

提供Python文件到C++代码的转换功能。
"""

import sys
import argparse
from pathlib import Path

from .compiler import MCPyCompiler
from .config import get_default_config


def str_to_bool(value: str) -> bool:
    """将字符串转换为布尔值"""
    if isinstance(value, bool):
        return value
    if value.lower() in ("true", "1", "yes", "on", "y"):
        return True
    elif value.lower() in ("false", "0", "no", "off", "n"):
        return False
    else:
        raise argparse.ArgumentTypeError(f"无效的布尔值: {value}")


def _emit_ir_if_requested(args, results):
    """输出 IR 文件（如果请求）"""
    try:
        if args.emit_ir and len(results) == 1 and results[0].ir_module is not None:
            import json

            ir_out = args.emit_ir
            ir_out.parent.mkdir(parents=True, exist_ok=True)
            # 简单可序列化视图
            irm = results[0].ir_module
            model = {
                "name": getattr(irm, "name", ""),
                "module_full_name": getattr(irm, "module_full_name", None),
                "classes": [
                    {
                        "name": getattr(c, "name", ""),
                        "kind": getattr(c, "kind", "class"),
                        "dbus_interface": getattr(c, "dbus_interface", None),
                        "path": getattr(c, "path", None),
                        "base_classes": getattr(c, "base_classes", []),
                    }
                    for c in getattr(irm, "classes", []) or []
                ],
                "includes": sorted(getattr(irm, "includes", set()) or []),
                "errors": getattr(irm, "errors", []),
            }
            ir_out.write_text(
                json.dumps(model, ensure_ascii=False, indent=2),
                encoding="utf-8",
            )
            if args.verbose:
                print(f"📝 已写出 IR: {ir_out}")
    except Exception as e:
        print(f"⚠️ 写出 IR 失败: {e}")


def _print_compilation_success(args, results):
    """打印编译成功信息"""
    for result in results:
        print(f"生成的文件: {result.output_files}")
        # 写入生成的文件到磁盘
        _write_generated_files(args, result)
        if args.verbose:
            _print_file_details(args, result.output_files)


def _write_generated_files(args, result):
    """将生成的文件写入磁盘"""
    if not result.generated_contents:
        return

    # 确保输出目录存在
    args.output.mkdir(parents=True, exist_ok=True)

    # 如果指定了头文件输出目录，确保它也存在
    if args.header_output:
        Path(args.header_output).mkdir(parents=True, exist_ok=True)

    for file_path, content in result.generated_contents.items():
        # 根据文件扩展名决定输出路径
        if file_path.endswith(".h") and args.header_output:
            output_path = Path(args.header_output) / file_path
        else:
            output_path = args.output / file_path

        # 确保父目录存在
        output_path.parent.mkdir(parents=True, exist_ok=True)

        # 写入文件
        output_path.write_text(content, encoding="utf-8")


def _print_file_details(args, output_files):
    """打印文件详细信息"""
    for file_path in output_files:
        # 根据文件扩展名决定输出路径
        if file_path.endswith(".h") and args.header_output:
            output_path = Path(args.header_output) / file_path
        else:
            output_path = args.output / file_path
        if output_path.exists():
            print(f"📄 {file_path}: {output_path.stat().st_size} 字节")


def _print_compilation_errors(results):
    """打印编译错误信息"""
    print("❌ 编译失败:")
    for result in results:
        if not result.success:
            for error in result.errors:
                print(f"  {error}")


def _emit_depfile_if_requested(args, results, compiler):
    """输出 depfile 文件（如果请求）"""
    try:
        if args.emit_depfile and len(results) == 1 and results[0].ir_module is not None:
            irm = results[0].ir_module
            from pathlib import Path as _P

            src = _P(getattr(irm, "name", "snippet.py"))
            out_tag = (
                ""
                if "output_tag" not in compiler.config
                else compiler.config["output_tag"]
            ) or src.stem.lower()
            # 目标：生成的 .cpp 作为 target
            base = out_tag + ".cpp"
            # 依赖：同目录被 import 的兄弟 .py 文件
            deps = []
            src_dir = src.parent
            import_info = getattr(irm, "import_info", None)
            if import_info:
                # from-import 可能指向同目录模块名
                for mod_name, sym in getattr(import_info, "from_imports", []) or []:
                    if not mod_name and sym:
                        cand = src_dir / f"{sym}.py"
                        if cand.exists():
                            deps.append(str(cand))
                    elif isinstance(mod_name, str) and mod_name:
                        cand = src_dir / f"{mod_name}.py"
                        if cand.exists():
                            deps.append(str(cand))
                for mod_name in getattr(import_info, "imports", []) or []:
                    if isinstance(mod_name, str) and mod_name:
                        cand = src_dir / f"{mod_name}.py"
                        if cand.exists():
                            deps.append(str(cand))
            deps = sorted(set(deps))
            depfile = args.emit_depfile
            depfile.parent.mkdir(parents=True, exist_ok=True)
            # Ninja depfile 语法：target: deps ...
            depfile.write_text(f"{base}: " + " ".join(deps) + "\n", encoding="utf-8")
            # if args.verbose:
            print(f"📝 已写出 depfile: {depfile} -> {base} 依赖 {len(deps)} 项")
    except Exception as e:
        print(f"⚠️ 写出 depfile 失败: {e}")


def main():
    """命令行入口点"""
    parser = argparse.ArgumentParser(
        description="MCPy - Python到C++代码生成器",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  mcpy input.py                    # 编译单个文件
  mcpy input.py -o output/         # 指定输出目录
  mcpy input.py --format           # 格式化生成的代码
  mcpy input.py -v                 # 详细输出
  mcpy input.py --format           # 格式化生成的代码
        """,
    )

    parser.add_argument("input_files", type=Path, nargs="*", help="输入的Python文件")

    parser.add_argument(
        "-o",
        "--output",
        type=Path,
        default="generated",
        help="输出目录 (默认: generated)",
    )

    # 注意：命名空间现在通过 @namespace 装饰器在代码中指定，不再需要 CLI 参数

    parser.add_argument("-t", "--template-dir", type=Path, help="模板目录路径")

    parser.add_argument("-v", "--verbose", action="store_true", help="详细输出")

    parser.add_argument(
        "--performance-stats", action="store_true", help="输出性能统计信息"
    )

    parser.add_argument("--memory-stats", action="store_true", help="输出内存使用统计")

    parser.add_argument(
        "--format", type=str_to_bool, default=True, help="格式化生成的代码 (默认: True)"
    )

    parser.add_argument(
        "--strict-typing",
        type=str_to_bool,
        default=False,
        help="启用严格类型检查（要求所有函数和变量都有类型注解）(默认: False)",
    )

    parser.add_argument(
        "--no-checks", action="store_true", help="关闭 Pass 语义检查（默认开启）"
    )

    parser.add_argument(
        "--passes-only", action="store_true", help="仅运行 Pass 管线，不生成代码"
    )

    parser.add_argument("--source-root", type=Path, help="源码根目录（用于计算包路径）")

    parser.add_argument(
        "--header-output",
        type=Path,
        help="头文件输出目录（如果指定，头文件将输出到此目录而不是 -o 目录）",
    )

    parser.add_argument("--version", action="version", version="MCPy 1.0.0")

    # 输出 IR/depfile 支持增量构建与调试
    parser.add_argument(
        "--emit-ir",
        type=Path,
        help="将 IR 模块以 JSON 形式写入指定文件（仅当单输入文件时建议使用）",
    )
    parser.add_argument(
        "--emit-depfile",
        type=Path,
        help="写出 Ninja depfile（.d），列出本编译单元依赖的同目录 .py 文件",
    )

    # 生成控制：输出名（避免多目标同名冲突）
    parser.add_argument(
        "--tag", type=str, help="强制输出基名，用于生成的 .cpp/.h/.pyi 前缀"
    )

    # Stubs 目录配置
    parser.add_argument(
        "--stubs-dir",
        type=Path,
        action="append",
        help="指定额外的stubs目录（可多次使用）",
    )

    args = parser.parse_args()

    # 检查输入文件
    if not args.input_files:
        parser.error("需要提供输入文件")

    if not all(f.exists() for f in args.input_files):
        print(f"错误: 输入文件不存在: {args.input_files}")
        sys.exit(1)

    # 准备配置
    config = get_default_config()
    config.update(
        {
            "output_dir": str(args.output),
            # 注意：不再从 CLI 设置 namespace，由 @namespace 装饰器控制
            "verbose": args.verbose,
            "format_code": args.format,
            "fail_on_checks": not args.no_checks,
            "passes_only": args.passes_only,
            "performance_stats": args.performance_stats,
            "memory_stats": args.memory_stats,
        }
    )

    # 更新编译器配置
    if "compiler" not in config:
        config["compiler"] = {}
    config["compiler"]["strict_typing"] = args.strict_typing

    if args.source_root:
        config["source_root"] = str(args.source_root)

    if args.tag:
        config["output_tag"] = args.tag

    if args.header_output:
        config["header_output"] = str(args.header_output)

    if args.stubs_dir:
        config["compiler"]["stubs_directories"] = [str(d) for d in args.stubs_dir]

    try:
        # 创建编译器并编译
        compiler = MCPyCompiler(config)

        if args.verbose:
            print(f"🚀 MCPy - Python到C++代码生成器")
            print(f"   输入文件: {args.input_files}")
            print(f"   输出目录: {args.output}")
            print(f"   使用 @namespace 装饰器指定命名空间")
            print()

        results = [compiler.compile_file(f) for f in args.input_files]
        all_success = all(r.success for r in results)
        if all_success:
            _print_compilation_success(args, results)
            _emit_ir_if_requested(args, results)
            _emit_depfile_if_requested(args, results, compiler)
        else:
            _print_compilation_errors(results)
            sys.exit(1)

    except Exception as e:
        print(f"❌ 编译失败: {e}")
        if args.verbose:
            import traceback

            traceback.print_exc()
        sys.exit(1)


if __name__ == "__main__":
    main()
